/*******************************************************************************
 * @file Philosoph.cpp
 * Implementation of methods for class CPhilosoph
 * 
 * @package Dining.
 * 
 * @author mk
 * @date 03/01/2008 13:50:21
 * @version 0.1
 ******************************************************************************/

#include "Philosoph.h"
#ifdef __MSVC_COMPILER_RECOGNIZES_CORRECT_FORWARD_DECLARATION__
#include "Spoon.h"
#endif
#include "Food.h"
#include "LockingPtr.h"
#include "Assert.h"
#include "Logging.h"
#include "Globals.h"

#include <boost/thread/thread.hpp>
#include <boost/thread/xtime.hpp>
#include <boost/thread/mutex.hpp>

using namespace Dining;

CPhilosoph::~CPhilosoph()
{
	TRACE_FUNC("CPhilosoph::~CPhilosoph");

	LOG_INFO("Philosoph " <<
		m_Id <<
		" was destroyed.");
}

CPhilosoph::CPhilosoph(const unsigned int anId, 
	CSpoon& aLeft,
	CSpoon& aRight,
	volatile CFood& aFood) : m_Id(anId),
					  m_Left(aLeft),
					  m_Right(aRight),
					  m_Food(aFood),
					  m_Consumed(0)
{
	TRACE_FUNC("CPhilosoph::CPhilosoph");
	
	LOG_INFO("Philosoph " <<
		m_Id <<
		" was created.");
}

void CPhilosoph::operator()()
{
	TRACE_FUNC("CPhilosoph::operator()");

	LOG_INFO("Philosoph " << m_Id << " starts dinner.");

	while(true)
	{
		{
#ifdef __USE_MUTEX_SPOON__
			boost::mutex::scoped_lock lock_left(m_Left);
			boost::mutex::scoped_lock lock_right(m_Right);
#else
			LockingPtr<CSpoon> left(m_Left, m_Left.m_Mutex);
			LockingPtr<CSpoon> right(m_Right, m_Right.m_Mutex);
#endif
			/** Spoons can be used exclusively now. */

			if (m_Food.Take())
			{
				this->Eat();
			}
			else
			{
				break;	// Everything was eaten.
			}
		}
		this->Think();
	}

	LOG_INFO("Philosoph " << m_Id << " finishs dinner.");
}

void CPhilosoph::Eat()
{
	TRACE_FUNC("CPhilosoph::Eat");
	
	/** Increase the portion counter. */
	m_Consumed++;

	const int delay = 1;	// 1 seconds
	
	boost::xtime xt;
	boost::xtime_get(&xt, boost::TIME_UTC);
	xt.sec += delay;
	
	LOG_INFO("Philosoph " <<
		m_Id <<
		" is eating his " <<
		m_Consumed <<
		" portion.");
	
	/** Let the philosopher sleep some time for "faking" to be busy. */
	boost::thread::sleep(xt);
}

void CPhilosoph::Think()
{
	TRACE_FUNC("CPhilosoph::Think");
	
	const int delay = 1;	// 1 seconds
	
	boost::xtime xt;
	boost::xtime_get(&xt, boost::TIME_UTC);
	xt.sec += delay;
	
	LOG_INFO("Philosoph " <<
		m_Id <<
		" is thinking.");
	
	/** Let the philosopher think some time. */
	boost::thread::sleep(xt);
}
